Erros comuns de tipagem em projetos Django e como corrigi-los

#public

Essa documentação tem como objetivo ajudar a corrigir erros de tipagem apontados pelo pyright em projetos Django.

Aqui você encontra exemplos de como corrigir alguns problemas comuns. Leia também a documentação do django-types para saber mais.

Usar propriedades de objetos Lazy

Algumas instâncias de classes que herdam de django.utils.functional.Lazy podem apresentar erros de tipagem, exemplo:

Cannot access member "url" for type "PublicMediaStorage"
  Member "url" is unknown

Para corrigir altere o lugar onde é instanciado o objeto lazy para definir o tipo concreto dele, exemplo:

-public_media_storage: Storage = PublicMediaStorage()
+public_media_storage: Storage = cast(Storage, PublicMediaStorage())

Acessar ID de uma ForeignKey

Por padrão o pyright não conhece os campos _id das ForeignKeys. Então você tiver um model com uma ForeignKey, por exemplo para User.

class Profile(models.Model):
   user = models.ForeignKey('auth.User')

E tentar fazer:

def example(profile: Profile):
    profile.user_id

Você vai receber o erro:

Cannot access member "user_id" for type "Profile"
Member "user_id" is unknown

Para que o pyright conheça o campo user_id adicione ele no modelo.

 class Profile(models.Model):
+    user_id: int 
     user = models.ForeignKey('auth.User')     

Acessar id de modelos

O pyright não conhece o campos id dos modelos. A seguinte mensagem de erro aparece quando você tem acessa o id de um model.

Cannot access member "id" for type "Profile"
Member "id" is unknown

Para corrigir use o atributo pk presente nos models, exemplo:

def example(profile: Profile):
    profile.pk

Ou, para corrigir códigos legados, adiciona a anotação de tipo id: int na definição do modelo.

 class Profile(models.Model):
+    id: int 

Acessar propriedade de modelo que pode ser nula

Se um campo de modelo for definido como null=True, como abaixo:

class Profile(models.Model):
    birth_date = models.DateField(null=True)

Ao tentar usar ele como abaixo:

def example(profile: Profile):
    profile.birth_date.strftime('%d/%m/%Y')

Você pode receber o erro: “strftime” is not a known member of “None”

Para resolver isso você pode:

(1) Adicionar uma verificação que garante que ao acessar o campo ele não seja nulo.

def example(profile: Profile):
    if profile.birth_date is None:
        profile.birth_date.strftime('%d/%m/%Y')
    # OU lançar uma exception
    if profile.birth_date is None:
        raise ValueError('...')

(2) Caso o modelo realmente não possa receber valores nulo nesse campo, fazer uma migração para garantir isso a nível de banco de dados.

-    birth_date = models.DateField(null=True, blank=True)
+    birth_date = models.DateField(null=False, blank=True)

[!danger] Migrações são perigosas Tenha em mente que a migração pode ser lenta para tabelas grandes e pode causar lock no banco de dados. Não faça a migração se não tiver segurança do que vai acontecer.